/**
 * Copyright 2006 gworks.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 */
package au.com.gworks.jump.app.util;

import java.io.IOException;
import java.security.Principal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TimeZone;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.javaongems.runtime.http.ExpressRedirector;
import org.javaongems.runtime.io.PathUtils;
import org.javaongems.runtime.lang.StringUtils;
import org.javaongems.server.PrincipalUtils;

import au.com.gworks.jump.io.UnauthorisedAccess;
import au.com.gworks.jump.util.TimeUtils;


final public class EnvironmentUtils {
    private static final Logger LOGGER = Logger.getLogger(EnvironmentUtils.class);

	final static public String ENV_REV_SPECIFIER_BEGIN = "(";
	final static public String ENV_REV_SPECIFIER_END = ")";

	final static public char ENV_REV_SPECIFIER_DATE = '@';
	final static public char ENV_REV_SPECIFIER_REVISION = 'r';

	final static public String CHANGECOMMENT_PARAM = "changeComment";
	final static public String ACTION_PARAM = "action";
	final static public String EXPLORE_VALUE = "explore";
	final static public String SEARCH_VALUE = "search";
	final static public String EXPLORE_QUERYSTRING = "?" + ACTION_PARAM + "=" + EXPLORE_VALUE;
	final static public String SEARCH_QUERYSTRING = "?" + ACTION_PARAM + "=" + SEARCH_VALUE;

	final static public String SRC_PARAM = "src";

	final static public String EMBEDD_DOC_REF_PARAMVAL = "?" + SRC_PARAM + "=true";

	final static public String ROOT_PARAM = "root-param";
	final static public String WIKI_PARAM = "wiki-param";
	final static public String SVN_PARAM = "svn-param";
	final static public String WEBDAV_PARAM = "webdav-param";
	final static public String FILES_PARAM = "files-param";

	final static public String ENV_NAMESPACE = "env.namespace";
	final static public String ENV_WAS_NAMESPACE_SPECIFIED = "env.was-namespace-specified";
	final static public String ENV_ORIGINAL_PATH_INFO = "env.original-path-info";
	
	final static public String ENV_UNAUTHORISED_ACCESS_EXCEPTION = "env.unauthorised-access-exception";
	final static public String ENV_REQUEST_PATH_INFO_OBJ = "env.request-path-info-object";
	
	final static public String ENV_APPL_CTX = "env.appl.ctx";
	final static public String ENV_NON_REV_DOC_CTX = "env.non-rev.doc.ctx";
	final static public String ENV_REV_DOC_CTX = "env.rev.doc.ctx";
	final static public String ENV_REV_LAST_CHANGED = "env.rev.last.changed";
	final static public String ENV_TOGGLE_REV_DOC_CTX = "env.toggle.rev.doc.ctx";
	final static public String ENV_REQUEST_QUERYSTRING = "env.request.query-string";
	final static public String ENV_REQUEST_TOGGLE_URI = "env.toggle.request.uri";
	final static public String ENV_REQUEST_TOGGLE_URI_WITH_QS = "env.toggle.request.uri-qs";
	final static public String ENV_REQUEST_ISAUTHENTICATED = "env.request.is-authenticated";
	final static public String ENV_REQUEST_REVISION = "env.request.revision";
	final static public String ENV_REQUEST_HEAD_REVISION = "env.request.head-revision";

	final static public String ENV_PAGE_INFO_OBJ = "env.page-info-object";
	
	final static public SimpleDateFormat REV_DATE_PRETTY_FORMATTER = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss");
	final static public SimpleDateFormat REV_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd");
	final static public SimpleDateFormat REV_EXT_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm");

	static Map contextMap = new HashMap();
	static Map toggleCtxMap = new HashMap();
	static Map switchCtxMap = new HashMap();
	static Map reverseToggleCtxMap = new HashMap();
	static Map pluginClassesMap = new HashMap();

	private EnvironmentUtils() {
		// nothing to do.
	}
	
	static public void initialiseApplicationContexts(Map contextMapArg,
			Map toggleCtxMapArg, Map switchCtxMapArg, Map pluginsMapArg) {
		contextMap.putAll(contextMapArg);
		toggleCtxMap.putAll(toggleCtxMapArg);
		switchCtxMap.putAll(switchCtxMapArg);
		Iterator itr = toggleCtxMap.keySet().iterator();
		while (itr.hasNext()) {
			Object name = itr.next();
			Object val = toggleCtxMap.get(name);
			reverseToggleCtxMap.put(val, name);
		}
		pluginClassesMap.putAll(pluginsMapArg);
	}
	
	static public String getApplicationContext() {
		String rootCtx = (String) contextMap.get(ROOT_PARAM);
		if (StringUtils.isEmpty(rootCtx))
			return "";
		return PathUtils.FORWARD_SLASH + rootCtx;
	}

	static public String getApplicationSubcontext(String subCtxParam, boolean includeRootCtx) {
		StringBuffer ctx = new StringBuffer();
		if (includeRootCtx)
			ctx.append(getApplicationContext());
		ctx.append(PathUtils.FORWARD_SLASH).append(contextMap.get(subCtxParam));
		return ctx.toString();
	}
	
	static public Object getPlugin(String ctx) {
		return pluginClassesMap.get(ctx);
	}

	static public void initialiseVisitationContext(HttpServletRequest req,
			HttpServletResponse resp, RequestInterceptorPlugin plugin) throws IOException, ServletException {
		RequestPathInfo rpi = null;
		try {
			rpi = plugin.createRequestPathInfo(req);
		} catch (UnauthorisedAccess err) {
			req.setAttribute(ENV_UNAUTHORISED_ACCESS_EXCEPTION, err);
			return;
		}
		redirectToSystemNamespaceIfRequired(req, resp, rpi);
		req.setAttribute(ENV_REQUEST_PATH_INFO_OBJ, rpi);
		
		String docCtx = rpi.getOrigRevisionContext(true);
		String lastChangedDateAsStr = getLastChangedDateAsString(rpi);
		
		String toggleDocCtx = docCtx;
		String altCtx = getAlternativeContext(docCtx, toggleCtxMap);
		if (!StringUtils.isEmpty(altCtx)) {
			redirectToAlternateContext(req, resp, docCtx, altCtx);
		} else {
			altCtx = getAlternativeContext(docCtx, switchCtxMap);
			if (!StringUtils.isEmpty(altCtx)) {
				redirectToAlternateContext(req, resp, docCtx, altCtx);
			}
			altCtx = getAlternativeContext(docCtx, reverseToggleCtxMap);
			if (!StringUtils.isEmpty(altCtx))
				toggleDocCtx = altCtx; 
		}
		prepareRequestAttributes(req, rpi, docCtx, lastChangedDateAsStr, toggleDocCtx);
	}

	static private void prepareRequestAttributes(HttpServletRequest req, RequestPathInfo rpi, String docCtx, String lastChangedDateAsStr, String toggleDocCtx) {
		Boolean isAuth = isAuthenticated(req) ? Boolean.TRUE : Boolean.FALSE;
		req.setAttribute(ENV_REQUEST_ISAUTHENTICATED, isAuth);
		String nonRevDocCtx = rpi.getOrigRevisionContext(false);

		req.setAttribute(ENV_APPL_CTX, getApplicationContext());
		req.setAttribute(ENV_REV_DOC_CTX, docCtx);
		req.setAttribute(ENV_REV_LAST_CHANGED, lastChangedDateAsStr);
		req.setAttribute(ENV_TOGGLE_REV_DOC_CTX, toggleDocCtx);
		req.setAttribute(ENV_NON_REV_DOC_CTX, nonRevDocCtx);

		String reqUri = req.getRequestURI();
		String queryString = req.getQueryString();
		req.setAttribute(ENV_REQUEST_QUERYSTRING, queryString);
		queryString = queryString == null ? "" : "?" + queryString;
		if (docCtx.equals(toggleDocCtx)) {
			req.setAttribute(ENV_REQUEST_TOGGLE_URI, reqUri);
			req.setAttribute(ENV_REQUEST_TOGGLE_URI_WITH_QS, reqUri + queryString);
		} else {
			String toggleReqUri = StringUtils.replace(reqUri, docCtx, toggleDocCtx, 1);
			req.setAttribute(ENV_REQUEST_TOGGLE_URI, toggleReqUri);
			req.setAttribute(ENV_REQUEST_TOGGLE_URI_WITH_QS, toggleReqUri + queryString);
		}
	}
	
	static private void redirectToSystemNamespaceIfRequired(HttpServletRequest req, HttpServletResponse resp, RequestPathInfo rpi) 
			throws IOException, ServletException {
		String origPathInfo = (String) req.getAttribute(ENV_ORIGINAL_PATH_INFO);
		if (origPathInfo == null)
			return;
		StringBuffer ret = new StringBuffer(origPathInfo);
		ret.insert(0, PathUtils.FORWARD_SLASH + rpi.namespace);
		String repl = ret.toString();
		redirectToAlternateContext(req, resp, origPathInfo, repl);
	}

	static public void initializeCharacterEncoding(HttpServletRequest req) {
		try {
			req.setCharacterEncoding("UTF-8");
		} catch (Exception err) {
			LOGGER.error("unable to set utf8 character encoding", err);
		}
	}

	static private String getLastChangedDateAsString(RequestPathInfo rpi) {
		String ret;
		try {
			long lastMod = TimeUtils.toZonedTime(rpi.attributes.getLastModified(), TimeZone.getDefault());
			ret = REV_DATE_PRETTY_FORMATTER.format(new Date(lastMod));
		} catch (Exception err) {
			ret = "Last changed date unavailable";
		}
		return ret;
	}

	static public String getAlternativeContext(String ctxToCheck, Map altCtxMap) {
		Iterator itr = altCtxMap.keySet().iterator();
		while (itr.hasNext()) {
			String key = (String) itr.next();
			String chgFromCtx = PathUtils.FORWARD_SLASH + key;
			if (ctxToCheck.startsWith(chgFromCtx)) {
				String chgToCtx = PathUtils.FORWARD_SLASH + (String) altCtxMap.get(key);
				String alt = StringUtils.replace(ctxToCheck, chgFromCtx, chgToCtx, 1);
				return alt;
			}
		}
		return null;
	}

	static public void redirectToAlternateContext(HttpServletRequest req,
			HttpServletResponse resp, String ctx, String changeToCtx)
			throws ServletException, IOException {
		String uri = req.getRequestURI();
		String queryString = req.getQueryString();
		uri = StringUtils.replace(uri, ctx, changeToCtx, 1);
		if (!StringUtils.isEmpty(queryString))
			uri = uri + "?" + queryString;
		uri = resp.encodeRedirectURL(uri);
		ExpressRedirector.redirect(resp, uri);
	}

	static public void redirectToContextWithEndSlashIfRequired(HttpServletRequest req,
			HttpServletResponse resp) throws ServletException, IOException {
		String uri = req.getRequestURI().toString();
		if (uri.endsWith(PathUtils.FORWARD_SLASH))
			return;
		uri = PathUtils.appendSlashIfRequired(uri);
		String queryString = req.getQueryString();
		if (!StringUtils.isEmpty(queryString))
			uri = uri + "?" + queryString;
		ExpressRedirector.redirect(resp, uri);
	}

	static public void redirectToSubContextUri(HttpServletResponse resp,
			String subCtxUri) throws IOException {
		String uri = getApplicationContext() + subCtxUri;
		ExpressRedirector.redirect(resp, uri);
	}

	static public boolean isAuthenticated(HttpServletRequest req) {
		Principal curr = PrincipalUtils.getPrincipal(req);
		boolean isVisitor = PrincipalUtils.isVisitorPrincipal(curr);
		return !isVisitor;
	}

	static public String getWikiDocSubcontext(HttpServletRequest req,
			String[] revIdStore) {
		RequestPathInfo info = (RequestPathInfo) req.getAttribute(EnvironmentUtils.ENV_REQUEST_PATH_INFO_OBJ);
		revIdStore[0] = info.revSpecifier;
		return info.context;
	}
}